/**********************************************
    vftl.c      130116
    based on pagemap.c
    realise some useful function of vftl
  ********************************************/

#include <stdlib.h>
#include <string.h>

#include "flash.h"
#include "pagemap.h"
#include "type.h"
#include "ssd_interface.h"


void trans_page_maptable_write(sect_t lsn, sect_t size, int mapdir_flag)
{
  int i;
  int lpn = lsn/SECT_NUM_PER_PAGE;
  int size_page = size/ SECT_NUM_PER_PAGE;
  int ppn;

  sect_t s_lsn;
  sect_t s_psn;
  sect_t s_psnl;
  sect_t lsns[SECT_NUM_PER_PAGE];

  memset(lsns, 0xFF, sizeof(lsns));

  s_lsn = lpn * SECT_NUM_PER_PAGE;


  if(curr_transpage_page_no >= SECT_NUM_PER_BLK){
  	if((curr_transpage_blk_no = nand_get_free_blk(0)) == -1){
		while (free_blk_num < 10 ){
			vftl_gc_run();
			}
		vftl_gc_get_free_blk(mapdir_flag);
  		}
	else {
		curr_transpage_page_no = 0;
		}
  	}

  if(trans_page_dir[lpn].free == 0){
  	s_psnl = trans_page_dir[lpn].ppn * SECT_NUM_PER_PAGE;
	for (i =0; i< SECT_NUM_PER_PAGE; i++){
		nand_invalidate(s_psnl + i, s_lsn + i);
		}
	nand_stat(OOB_WRITE);
  	}
  else {
  	trans_page_dir[lpn].free = 0;
  	}
  
  for(i =0; i < SECT_NUM_PER_PAGE; i++){
  	lsns[i] = s_lsn + i;
  	}
  //get the new page no
  s_psn = SECTOR(curr_transpage_blk_no,curr_transpage_page_no);
  ppn = s_psn /SECT_NUM_PER_PAGE;

  //update the transpage_maptable
  trans_page_dir[lpn].ppn = ppn;
  nand_page_write(s_psn, lsns, 0, mapdir_flag);
  curr_transpage_page_no += SECT_NUM_PER_PAGE;
  trans_page_dir[lpn].update = 0;
}

void trans_page_maptable_read(sect_t lsn, sect_t size, int mapdir_flag)
{
  int i;
  int lpn = lsn/SECT_NUM_PER_PAGE;
  int size_page = size/SECT_NUM_PER_PAGE;

  sect_t s_lsn;
  sect_t s_psn;
  sect_t lsns[SECT_NUM_PER_PAGE];

  memset(lsns, 0xff, sizeof(lsns));

  s_lsn = lpn * SECT_NUM_PER_PAGE;
  s_psn = trans_page_dir[lpn].ppn * SECT_NUM_PER_PAGE;

  for(i = 0; i<SECT_NUM_PER_PAGE;i++){
  	lsns[i] = s_lsn + i;
  	}
  nand_page_read(s_psn,lsns,0);

  curr_trans_maptable_no = lpn;
  
}

/*void clean_page_maptable_write(sect_t lsn, sect_t size, int mapdir_flag)
{
  int i;
  int ppn;
  int lpn = lsn/SECT_NUM_PER_PAGE;
  int size_page = size/ SECT_NUM_PER_PAGE;

  sect_t s_lsn;
  sect_t s_psn;
  sect_t s_psnl;
  sect_t lsns[SECT_NUM_PER_PAGE];

  memset(lsns, 0xFF, sizeof(lsns));
  
  if(curr_clean_transpage_page_no >= SECT_NUM_PER_BLK){
  	if((curr_clean_transpage_blk_no = nand_get_free_blk(0)) == -1){
		while (free_blk_num < 10){
			vftl_gc_run();
			}
		vftl_gc_get_free_blk(mapdir_flag);
  		}
	else {
		curr_clean_transpage_page_no = 0;
		}
  	}
  if(clean_page_info.free == 0){
	s_psnl = clean_page_info.ppn * SECT_NUM_PER_PAGE;
	for(i = 0; i< SECT_NUM_PER_PAGE; i++){
		nand_invalidate(s_psnl + i,s_lsn + i );
		}
	nand_stat(OOB_WRITE);
	}
  else{
	clean_page_info.free = 0;
	}	

  for(i =0; i < SECT_NUM_PER_PAGE; i++){
  	lsns[i] = s_lsn + i;
  	}

  s_psn = SECTOR(curr_clean_transpage_blk_no,curr_clean_transpage_page_no);
  ppn =  s_psn / SECT_NUM_PER_PAGE;

  clean_page_info.ppn = ppn;
  nand_page_write(s_psn, lsns, 0 , mapdir_flag);
  curr_clean_transpage_page_no += SECT_NUM_PER_PAGE;
  clean_page_info.updata = 0;

}
*/


void vftl_gc_get_free_blk(int mapdir_flag)
{
  if(mapdir_flag == 2){
  	if(curr_transpage_page_no >= SECT_NUM_PER_BLK){
		curr_transpage_blk_no = nand_get_free_blk(1);
		curr_transpage_page_no = 0;
		}
  	}
  else if(mapdir_flag == 1){
  	if(curr_datepage_page_no>= SECT_NUM_PER_BLK){
		curr_datapage_blk_no = nand_get_free_blk(1);
		curr_datepage_page_no = 0;
  		}	
  	}
  else if(mapdir_flag == 0){
  	if(curr_cold_date_page_no >= SECT_NUM_PER_BLK){
		curr_cold_data_blk_no = nand_get_max_free_blk(1);
		curr_cold_date_page_no = 0;
  		}
  	}
  	

}

void vftl_gc_cost_benefit()
{
   int max_cb = 0;
   int blk_cb;

   _u32 max_blk = -1, i;

   for (i = 0; i < nand_blk_num; i++) {
   	if(i == curr_transpage_blk_no
		|| i == curr_datapage_blk_no
		|| i == curr_cold_data_blk_no){
		continue;
		}

    blk_cb = nand_blk[i].ipc;
    if (blk_cb > max_cb) {
      max_cb = blk_cb;
      max_blk = i;
    }
  }

  ASSERT(max_blk != -1);
  ASSERT(nand_blk[max_blk].ipc > 0);
  return max_blk;


}

void vftl_gc_run()
{
	
	blk_t victim_blk_no;
	int merge_count;
	int i,z, j,m,q, benefit = 0;
	int k,old_flag,temp_arr[PAGE_NUM_PER_BLK],temp_arr1[PAGE_NUM_PER_BLK],map_arr[PAGE_NUM_PER_BLK]; 
	int valid_flag,pos;
	
	_u32 copy_lsn[SECT_NUM_PER_PAGE], copy[SECT_NUM_PER_PAGE];
	_u16 valid_sect_num,  l;//s;


	pos = 0;
	//merge_count = 0;
	victim_blk_no = vftl_gc_cost_benefit();
	memset(copy_lsn, 0xFF, sizeof (copy_lsn));
	
	//s = k = OFF_F_SECT(free_page_no[small]);
	
	/*if(!((s == 0) && (k == 0))){
	  printf("s && k should be 0\n");
	  exit(0);
	}*/
	
	//small = -1;
	
	for( q = 0; q < PAGE_NUM_PER_BLK; q++){
		//trans_page map block
		if(nand_blk[victim_blk_no].page_status[q] == 2){
			for(q = 0; q < 64; q++){
				if(nand_blk[victim_blk_no].page_status[q] != 2){
					printf("something corrupted2 = %d", victim_blk_no);
					}
				}
			}
		// hot data block
		else if(nand_blk[victim_blk_no].page_status[q] == 1){
			for( q =0; q <64 ; q++){
				if(nand_blk[victim_blk_no].page_status[q] != 1){
					printf("something corrupted1 = %d", victim_blk_no);
					}
				}
			}
		//cold data block
		else if(nand_blk[victim_blk_no].page_status[q] == 0){ 
			for( q = 0; q  < 64; q++) {
				if(nand_blk[victim_blk_no].page_status[q] != 0 ){
					printf("something corrupted0",victim_blk_no);
		  }
		}
		
	  }
	}
	

	for (i = 0; i < PAGE_NUM_PER_BLK; i++) 
	{
	  valid_flag = nand_oob_read( SECTOR(victim_blk_no, i * SECT_NUM_PER_PAGE));
	
	  if(valid_flag == 1)
	  {
		  valid_sect_num = nand_page_read( SECTOR(victim_blk_no, i * SECT_NUM_PER_PAGE), copy, 1);
	
		  merge_count++;
	
		  ASSERT(valid_sect_num == 4);
		  k=0;
		  for (j = 0; j < valid_sect_num; j++) {
			copy_lsn[k] = copy[j];
			k++;
		  }
		  if(nand_blk[victim_blk_no].page_status[i] == 2){
		  	vftl_gc_get_free_blk(2);
			trans_page_dir[(copy_lsn[0]/SECT_NUM_PER_PAGE)].ppn  = BLK_PAGE_NO_SECT(SECTOR(curr_transpage_blk_no, curr_transpage_page_no));
			vftl_pagemap[copy_lsn[0]/SECT_NUM_PER_PAGE].ppn = BLK_PAGE_NO_SECT(SECTOR(curr_transpage_blk_no, curr_transpage_page_no));
			nand_page_write(SECTOR(curr_transpage_blk_no, curr_transpage_page_no) & (~OFF_MASK_SECT), copy_lsn, 1, 2);
			curr_transpage_page_no += SECT_NUM_PER_PAGE;
			}
		  else{
		  	vftl_gc_get_free_blk(0);
			vftl_pagemap[BLK_PAGE_NO_SECT(copy_lsn[0])].ppn = BLK_PAGE_NO_SECT(SECTOR(curr_cold_data_blk_no, curr_cold_date_page_no));
			nand_page_write(SECTOR(curr_cold_data_blk_no, curr_cold_date_page_no) & (~OFF_MASK_SECT), copy_lsn, 1, 1);
			curr_cold_date_page_no += SECT_NUM_PER_PAGE;
			if((vftl_pagemap[BLK_PAGE_NO_SECT(copy_lsn[0])].map_status == MAP_REAL) 
				|| (vftl_pagemap[BLK_PAGE_NO_SECT(copy_lsn[0])].map_status == MAP_GHOST)) {
				delay_flash_update++;
			  }
			else {
				map_arr[pos] = copy_lsn[s];
				pos++;
				}
			}
	  }
	}
	
	for(i=0;i < PAGE_NUM_PER_BLK;i++) {
		temp_arr[i]=-1;
	}
	k=0;
	for(i =0 ; i < pos; i++) {
		old_flag = 0;
		for( j = 0 ; j < k; j++) {
			 if(temp_arr[j] == trans_page_dir[((map_arr[i]/SECT_NUM_PER_PAGE)/MAP_ENTRIES_PER_PAGE)].ppn) {
				  if(temp_arr[j] == -1){
						printf("something wrong");
						ASSERT(0);
				  }
				  old_flag = 1;
				  break;
			 }
		}
		if( old_flag == 0 ) {
			 temp_arr[k] = trans_page_dir[((map_arr[i]/SECT_NUM_PER_PAGE)/MAP_ENTRIES_PER_PAGE)].ppn;
			 temp_arr1[k] = map_arr[i];
			 k++;
		}
		else
		  save_count++;
	}
	
	for ( i=0; i < k; i++) {
		nand_page_read(temp_arr[i]*SECT_NUM_PER_PAGE,copy,1);
		
		for(m = 0; m<SECT_NUM_PER_PAGE; m++){
			nand_invalidate(trans_page_dir[((temp_arr1[i]/SECT_NUM_PER_PAGE)/MAP_ENTRIES_PER_PAGE)].ppn*SECT_NUM_PER_PAGE+m, copy[m]);
			}
		nand_stat(OOB_WRITE);
		if(curr_transpage_page_no >= SECT_NUM_PER_BLK){
			curr_transpage_blk_no = nand_get_free_blk(1);
			curr_transpage_page_no = 0;
			}
		
		trans_page_dir[((temp_arr1[i]/SECT_NUM_PER_PAGE)/MAP_ENTRIES_PER_PAGE)].ppn  = BLK_PAGE_NO_SECT(SECTOR(curr_transpage_blk_no, curr_transpage_page_no));
		vftl_pagemap[((temp_arr1[i]/SECT_NUM_PER_PAGE)/MAP_ENTRIES_PER_PAGE)].ppn = BLK_PAGE_NO_SECT(SECTOR(curr_transpage_blk_no, curr_transpage_page_no));
		nand_page_write(SECTOR(curr_transpage_blk_no, curr_transpage_page_no) & (~OFF_MASK_SECT), copy, 1, 2);
		curr_transpage_page_no += SECT_NUM_PER_PAGE;
		//}
	
	/*if(merge_count == 0 ) 
	  merge_switch_num++;
	else if(merge_count > 0 && merge_count < PAGE_NUM_PER_BLK)
	  merge_partial_num++;
	else if(merge_count == PAGE_NUM_PER_BLK)
	  merge_full_num++;
	else if(merge_count > PAGE_NUM_PER_BLK){
	  printf("merge_count =%d PAGE_NUM_PER_BLK=%d",merge_count,PAGE_NUM_PER_BLK);
	  ASSERT(0);*/
		}
	nand_erase(victim_blk_no);
	
	//return (benefit + 1);
}

int vftl_init(blk_t blk_num, blk_t extra_num)
{
	int i;
	int trans_pagemap_num;//translation page num at systm initial

	//create the translation page mapdir
	vftl_pagemap_num = blk_num * PAGE_NUM_PER_BLK;//num of pages
	trans_pagemap_num = vftl_pagemap_num / MAP_ENTRIES_PER_PAGE;//gtd size == num of translation pages

	if((vftl_pagemap_num % MAP_ENTRIES_PER_PAGE) != 0){
    		printf("vftl_pagemap_num % MAP_ENTRIES_PER_PAGE is not zero\n"); 
   		trans_pagemap_num++;
  	}

	vftl_pagemap = (struct vftl_pm_entry *) malloc(sizeof (struct vftl_pm_entry) * vftl_pagemap_num);
	//create the GTD page mapdir
	trans_page_dir = (struct trans_page_mapdir *) malloc(sizeof (struct trans_page_mapdir) * trans_pagemap_num);
	//clean_page_dir = (struct trans_page_mapdir *) malloc(sizeof (struct clean_page_mapdir) * CLEAN_PAGE_MAPDIR_NUM_PER_PAGE);//clean_page size is alwz 512 entry
	
	if ((vftl_pagemap == NULL) || (trans_page_dir == NULL) || (clean_page_dir == NULL) ) {
   		return -1;
 	}
	memset(vftl_pagemap, 0xFF, sizeof (struct vftl_pm_entry) * vftl_pagemap_num);
 	memset(trans_page_dir,  0xFF, sizeof (struct trans_page_mapdir) * trans_pagemap_num);
  //	memset(clean_page_dir,  0xFF, sizeof (struct clean_page_mapdir) * CLEAN_PAGE_MAPDIR_NUM_PER_PAGE);

	 //youkim: 1st map table 
  	TOTAL_MAP_ENTRIES = vftl_pagemap_num;

 	for(i = 0; i<TOTAL_MAP_ENTRIES; i++){
    		vftl_pagemap[i].cache_status = 0;
    		vftl_pagemap[i].cache_age = 0;
    		vftl_pagemap[i].map_status = 0;
    		vftl_pagemap[i].map_age = 0;
    		vftl_pagemap[i].update = 0;
			//vftl_pagemap[i].local = 0;
  	}

	for(i = 0; i<trans_pagemap_num; i++){
   		trans_page_dir[i].update = 0;
  	}

  /*	for(i = 0; i<CLEAN_PAGE_MAPDIR_NUM_PER_PAGE; i++){
    		clean_page_dir[i].update = 0;
  	}*/

	
	
	curr_transpage_blk_no = nand_get_free_blk(0);
  	curr_transpage_page_no = 0;
	curr_datapage_blk_no = nand_get_free_blk(0);
  	curr_datepage_page_no = 0;
	curr_cold_data_blk_no = nand_get_free_blk(0);
	curr_cold_date_page_no = 0;
        

	//initialize variables
  	MAP_REAL_NUM_ENTRIES = 0;
  	MAP_GHOST_NUM_ENTRIES = 0;
  	CACHE_NUM_ENTRIES = 0;
	MAP_CLEAN_NUM_ENTRIES = 0;
	curr_trans_maptable_no = 0;
	//curr_clean_maptable_no = -1;
	curr_trans_mpatable_update_mark = 0;
	
	//initialize variables
	cache_hit = 0;
	flash_hit = 0;
	evict = 0;
	read_cache_hit = 0;
	write_cache_hit = 0;
	write_count =0;
	read_count = 0;
	save_count = 0;
	update_evict = 0;
	

	//initialize 2nd map table--write the translation page in flash
  	for(i = 0; i<trans_pagemap_num; i++){
		ASSERT(MAP_ENTRIES_PER_PAGE == 512);
		vftl_write(i*SECT_NUM_PER_PAGE, SECT_NUM_PER_PAGE, 2);//2 for map;
  	}
	
	//for clean-translation-page
	//clean_page_info.lpn = trans_pagemap_num;
	//vftl_write(clean_page_info.lpn * SECT_NUM_PER_PAGE, SECT_NUM_PER_PAGE, 2);

  	
	return 0;
}

size_t vftl_write(sect_t lsn, sect_t size, int mapdir_flag)
{
	int i;
	int lpn = lsn/SECT_NUM_PER_PAGE;// logical page number
	int size_page = size/SECT_NUM_PER_PAGE;// size in page 
	int ppn;
	int sect_num = SECT_NUM_PER_PAGE;
	int demand_trans_page_no;

	sect_t lsns[SECT_NUM_PER_PAGE];
	sect_t s_lsn;//starting logical sector number
	sect_t s_psn;//starting physical sector number
	sect_t s_psn1;
	memset(lsns, 0xFF, sizeof (lsns));

	ASSERT(lpn < vftl_pagemap_num);
	ASSERT(lpn + size_page <= vftl_pagemap_num);
	
	demand_trans_page_no = lpn / MAP_ENTRIES_PER_PAGE;
	
	s_lsn = lpn * SECT_NUM_PER_PAGE;

	//1. find a new page to write-------not finish------//
	if(curr_datepage_page_no >= SECT_NUM_PER_BLK){
		if((curr_datapage_blk_no = nand_get_free_blk(0)) == -1){
			while(free_blk_num < 10){
				vftl_gc_run();
				}
			vftl_gc_get_free_blk(mapdir_flag);
			}
		else {
			curr_datepage_page_no = 0;
			}
		}
	
	//2. check if have been written, if yes and invalidate the page
	if(vftl_pagemap[lpn].free == 0){
		s_psn1 = vftl_pagemap[lpn].ppn * SECT_NUM_PER_PAGE;
		for(i = 0; i < SECT_NUM_PER_PAGE; i++){
			nand_invalidate(s_psn1 + i,s_lsn + i);
			}
		nand_stat(3);
		}
	else {
		vftl_pagemap[lpn].free = 0;
	}
	
    //3. write new data to a new page
    s_psn = SECTOR(curr_datapage_blk_no,curr_datepage_page_no);
	for(i = 0; i < SECT_NUM_PER_PAGE; i++){
		lsns[i] = s_lsn + i;
		}
	ppn = s_psn / SECT_NUM_PER_PAGE;
	
	if(s_psn % 4 != 0){
		printf("s_psn:%d\n",s_psn);
	}

	nand_page_write(s_psn, lsns, 0, mapdir_flag);
	curr_datepage_page_no += SECT_NUM_PER_PAGE;
	
	//4. updata the vftl_pagemap
	vftl_pagemap[lpn].ppn = ppn;
	
	//mark for the spcmt 
	if(curr_trans_mpatable_update_mark == 1){
		trans_page_dir[demand_trans_page_no].update = 1;
		}
	
	return sect_num;	

}

size_t vftl_read(sect_t lsn, sect_t size, int mapdir_flag)
{
	int i;
  	int lpn = lsn/SECT_NUM_PER_PAGE; // logical page number
  	int size_page = size/SECT_NUM_PER_PAGE; // size in page 
  	int sect_num;
	
	sect_t s_lsn;	// starting logical sector number
  	sect_t s_psn; // starting physical sector number

	sect_t lsns[SECT_NUM_PER_PAGE];
	
	ASSERT(lpn < vftl_pagemap_num);
	ASSERT(lpn + size_page <= vftl_pagemap_num);
	
	memset (lsns, 0xFF, sizeof (lsns));
	
	sect_num = (size < SECT_NUM_PER_PAGE) ? size : SECT_NUM_PER_PAGE;

	s_psn = vftl_pagemap[lpn].ppn * SECT_NUM_PER_PAGE;
 	s_lsn = lpn * SECT_NUM_PER_PAGE;
  	for (i = 0; i < SECT_NUM_PER_PAGE; i++) {
    		lsns[i] = s_lsn + i;
  	}

  	nand_page_read(s_psn, lsns, 0);

  	return sect_num;
	}



void vftl_end()
{
	if(vftl_pagemap != NULL){
		free(vftl_pagemap);
		free(trans_page_dir);
		free(clean_page_dir);
	}
	vftl_pagemap_num = 0;

}

void vftl_pagemap_reset()
{
	cache_hit = 0;
  	flash_hit = 0;  
  	evict = 0;
  	delay_flash_update = 0; 
  	read_count =0;
  	write_count=0;
  	save_count = 0;
  	update_evict = 0;
  	
}




struct ftl_operation vftl_operation = {
  init:     vftl_init,
  read:  vftl_read,
  write: vftl_write,
  end:   vftl_end
};

struct ftl_operation * vftl_setup()
{
  return &vftl_opm_operation;
}

